import * as tus from 'tus-js-client';
import SparkMD5 from 'spark-md5';

// === DOM Elements ===
const dropArea = document.getElementById('drop-area');
const fileElem = document.getElementById('fileElem');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resumeBtn = document.getElementById('resumeBtn');
const cancelBtn = document.getElementById('cancelBtn');
const progressBar = document.getElementById('progressBar');
const statusDiv = document.getElementById('status');

const fileUrl = document.getElementById('fileUrl');
const downloadBtn = document.getElementById('downloadBtn');
const pauseDownloadBtn = document.getElementById('pauseDownloadBtn');
const resumeDownloadBtn = document.getElementById('resumeDownloadBtn');
const cancelDownloadBtn = document.getElementById('cancelDownloadBtn');
const downloadProgressBar = document.getElementById('downloadProgressBar');
const downloadStatus = document.getElementById('downloadStatus');
const downloadInfo = document.getElementById('downloadInfo');

let upload = null;
let selectedFile = null;
let downloadController = null;

// === 格式化工具 ===
function formatBytes(bytes) {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

function formatTime(date) {
  return new Date(date).toLocaleTimeString();
}

function formatDuration(ms) {
  if (ms < 0) return '-';
  const seconds = Math.floor((ms / 1000) % 60);
  const minutes = Math.floor((ms / (1000 * 60)) % 60);
  const hours = Math.floor(ms / (1000 * 60 * 60));
  return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
}

// === 拖拽上传逻辑 ===
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(event =>
  dropArea.addEventListener(event, e => {
    e.preventDefault();
    e.stopPropagation();
  })
);

dropArea.addEventListener('dragover', () => dropArea.classList.add('highlight'));
dropArea.addEventListener('dragleave', () => dropArea.classList.remove('highlight'));
dropArea.addEventListener('drop', e => {
  dropArea.classList.remove('highlight');
  handleFiles(e.dataTransfer.files);
});

dropArea.onclick = () => fileElem.click();
fileElem.onchange = e => handleFiles(e.target.files);

function handleFiles(files) {
  if (files.length > 0) {
    selectedFile = files[0];
    statusDiv.textContent = `已选择文件: ${selectedFile.name} (${formatBytes(selectedFile.size)})`;
    startBtn.disabled = false;
  }
}

// === 上传功能 ===
let uploadStartTime = null;

startBtn.onclick = startUpload;
pauseBtn.onclick = () => upload?.abort?.();
resumeBtn.onclick = () => upload?.start?.();
cancelBtn.onclick = () => {
  upload?.abort?.();
  upload = null;
  resetUI();
};

function startUpload() {
  if (!selectedFile) return;
  startBtn.disabled = true;
  statusDiv.textContent = "正在计算校验和...";
  uploadStartTime = Date.now();
  document.getElementById('uploadStartTime').textContent = formatTime(uploadStartTime);

  calculateMD5(selectedFile, pct => {
    statusDiv.textContent = `📊 计算 MD5: ${pct.toFixed(1)}%`;
  }, checksum => {
    statusDiv.textContent = "✅ 校验和完成，上传中...";
    upload = new tus.Upload(selectedFile, {
      endpoint: "http://localhost:8080/uploads",
      retryDelays: [0, 1000, 3000],
      chunkSize: 10 * 1024 * 1024,
      metadata: {
        name: selectedFile.name,
        checksum: `md5 ${checksum}`
      },
      onError: err => {
        statusDiv.textContent = `❌ 上传失败: ${err.message}`;
        updateUploadTimeUI();
        resetUI();
      },
      onProgress: (uploaded, total) => {
        const pct = ((uploaded / total) * 100).toFixed(2);
        progressBar.value = pct;
        statusDiv.textContent = `📤 上传中: ${pct}% (${formatBytes(uploaded)} / ${formatBytes(total)})`;
      },
      onSuccess: () => {
        const url = upload.url;
        statusDiv.innerHTML = `✅ 上传成功！<br>访问地址: <a href="${url}" target="_blank">${url}</a>`;
        updateUploadTimeUI();
        pauseBtn.disabled = true;
        resumeBtn.disabled = true;
        cancelBtn.disabled = true;
      }
    });
    upload.start();
  }, err => {
    statusDiv.textContent = `❌ MD5 计算失败: ${err.message}`;
    resetUI();
  });
}

function updateUploadTimeUI() {
  const elapsed = formatDuration(Date.now() - uploadStartTime);
  document.getElementById('uploadDuration').textContent = elapsed;
  document.getElementById('uploadTimeInfo').classList.remove('hidden');
}

function calculateMD5(file, onProgress, onSuccess, onError = console.error) {
  const chunkSize = 500 * 1024 * 1024;
  const spark = new SparkMD5.ArrayBuffer();
  let cursor = 0;
  const reader = new FileReader();

  function next() {
    if (cursor >= file.size) {
      onSuccess(spark.end());
      return;
    }
    const slice = file.slice(cursor, cursor + chunkSize);
    reader.onload = e => {
      spark.append(e.target.result);
      cursor += slice.size;
      if (onProgress) onProgress((cursor / file.size) * 100);
      next();
    };
    reader.onerror = onError;
    reader.readAsArrayBuffer(slice);
  }
  next();
}

// === 流式下载管理器（优化版）===
class DownloadManager {
  constructor(url, fileName = null) {
    this.url = url;
    this.fileName = fileName;
    this.chunks = [];
    this.totalSize = 0;
    this.downloadedSize = 0;
    this.chunkSize = 1024 * 1024*10; // 10MB
    this.isPaused = false;
    this.isCancelled = false;
    this.isCompleted = false;

    // 文件系统
    this.fileHandle = null;
    this.writable = null;

    // 并发控制
    this.maxConcurrent =  navigator.hardwareConcurrency || 3;
    this.downloading = new Set();
    this.nextIndex = 0;

    // 时间
    this.startTime = null;
    this.endTime = null;

    // 请求控制器
    this.controller = new AbortController(); // 用于 fetch 中断
  }

  async start() {
    try {
      const res = await fetch(this.url, { method: 'HEAD', signal: this.controller.signal });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      if (res.headers.get('accept-ranges') !== 'bytes') throw new Error('服务器不支持断点续传');

      this.totalSize = parseInt(res.headers.get('content-length'), 10);

      const cd = res.headers.get('content-disposition');
      if (cd) {
        const match = cd.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i);
        if (match) this.fileName = match[1].replace(/['"]/g, '');
      }
      this.fileName = this.fileName || this.url.split('/').pop() || 'download.bin';

      this.initializeChunks();

      const saved = this.loadState();
      if (saved) {
        this.chunks = saved.chunks;
        this.downloadedSize = saved.downloadedSize;
        this.startTime = saved.startTime || Date.now();
      } else {
        this.startTime = Date.now();
        this.saveState();
      }

      this.updateTimeUI();

      if (!this.fileHandle) {
        this.fileHandle = await this.createFileHandle();
        this.writable = await this.fileHandle.createWritable();
      }

    } catch (err) {
      if (err.name === 'AbortError') throw err;
      throw err;
    }
  }

  async createFileHandle() {
    if (!('showSaveFilePicker' in window)) {
      throw new Error('此浏览器不支持文件系统访问 API，请使用 Chrome/Edge');
    }
    return await window.showSaveFilePicker({
      suggestedName: this.fileName
    });
  }

  initializeChunks() {
    this.chunks = [];
    for (let start = 0; start < this.totalSize; start += this.chunkSize) {
      const end = Math.min(start + this.chunkSize - 1, this.totalSize - 1);
      this.chunks.push({ start, end, downloaded: false });
    }
  }

  async safeWrite(chunk) {
    if (!this.writable) throw new Error('文件流已关闭，无法写入');
    await this.writable.write(chunk);
  }

  async mergeAndDownload() {
    if (this.isCompleted || !this.writable) return;

    try {
      if (!this.startTime) {
        this.startTime = Date.now();
        this.saveState();
        this.updateTimeUI();
      }

      this.isPaused = false;
      this.isCancelled = false;
      this.nextIndex = 0;
      this.downloading.clear();
      this.controller = new AbortController(); // 新控制器

      const downloadChunk = async () => {
        while (this.nextIndex < this.chunks.length && !this.isPaused && !this.isCancelled) {
          const i = this.nextIndex++;
          if (this.chunks[i].downloaded) continue;

          this.downloading.add(i);
          try {
            const { start, end } = this.chunks[i];
            const res = await fetch(this.url, {
              headers: { 'Range': `bytes=${start}-${end}` },
              signal: this.controller.signal
            });

            if (!res.ok) throw new Error(`分片 ${i} 失败: ${res.status}`);

            const arrayBuffer = await res.arrayBuffer();
            await this.safeWrite(new Uint8Array(arrayBuffer));

            this.chunks[i].downloaded = true;
            this.downloadedSize += arrayBuffer.byteLength;
            this.saveState();
            this.updateProgress();
          } catch (err) {
            if (this.isPaused || this.isCancelled) return;
            throw err;
          } finally {
            this.downloading.delete(i);
          }
        }
      };

      const workers = Array(this.maxConcurrent).fill(null).map(() => downloadChunk());
      await Promise.all(workers);

      if (!this.isPaused && !this.isCancelled) {
        await this.writable.close();
        this.isCompleted = true;
        this.endTime = Date.now();
        this.saveState();
        this.updateTimeUI();
        downloadStatus.textContent = '✅ 下载完成（流式写入）';
        this.cleanup();
      }

    } catch (err) {
      if (this.isPaused) {
        downloadStatus.textContent = '⏸️ 下载已暂停';
      } else if (this.isCancelled) {
        this.endTime = Date.now();
        this.saveState();
        this.updateTimeUI();
        downloadStatus.textContent = '⏹️ 下载已取消';
        this.cleanup();
      } else {
        downloadStatus.textContent = `❌ 下载失败: ${err.message}`;
      }
      throw err;
    }
  }

  updateProgress() {
    const pct = ((this.downloadedSize / this.totalSize) * 100).toFixed(2);
    downloadProgressBar.value = pct;
    downloadInfo.textContent = `已下载: ${formatBytes(this.downloadedSize)} / ${formatBytes(this.totalSize)}`;
    downloadStatus.textContent = `📥 下载中: ${pct}%`;
  }

  updateTimeUI() {
    if (this.startTime) {
      document.getElementById('downloadStartTime').textContent = formatTime(this.startTime);
      document.getElementById('downloadTimeInfo').classList.remove('hidden');
    }
    if (this.endTime) {
      document.getElementById('downloadDuration').textContent = formatDuration(this.endTime - this.startTime);
    } else if (this.startTime) {
      const elapsed = Date.now() - this.startTime;
      document.getElementById('downloadDuration').textContent = formatDuration(elapsed);
    }
  }

  saveState() {
    const state = {
      chunks: this.chunks,
      downloadedSize: this.downloadedSize,
      fileName: this.fileName,
      url: this.url,
      timestamp: Date.now(),
      startTime: this.startTime,
      endTime: this.endTime
    };
    localStorage.setItem(`dlstate_${btoa(this.url)}`, JSON.stringify(state));
  }

  loadState() {
    const raw = localStorage.getItem(`dlstate_${btoa(this.url)}`);
    if (!raw) return null;
    try {
      const state = JSON.parse(raw);
      if (state.chunks.length === this.chunks.length) {
        this.fileName = state.fileName;
        return state;
      }
    } catch (e) {}
    return null;
  }

  cleanup() {
    localStorage.removeItem(`dlstate_${btoa(this.url)}`);
    this.fileHandle = null;
    this.writable = null;
  }

  pause() {
    if (this.isPaused || this.isCompleted) return;
    this.isPaused = true;
    this.controller.abort(); // 中断所有请求
  }

  async resume() {
    if (!this.isPaused || !this.writable) return;
    this.isPaused = false;
    this.controller = new AbortController(); // 新控制器

    pauseDownloadBtn.disabled = false;
    resumeDownloadBtn.disabled = true;

    try {
      await this.mergeAndDownload();
    } catch (err) {
      if (!this.isPaused && !this.isCancelled) {
        downloadStatus.textContent = `❌ 恢复失败: ${err.message}`;
      }
    }
  }

  cancel() {
    this.isCancelled = true;
    this.isPaused = true;
    this.controller.abort();

    this.endTime = Date.now();
    this.saveState();
    this.updateTimeUI();
    this.cleanup();
    downloadStatus.textContent = '⏹️ 下载已取消';
    resetDownloadUI();
  }
}

// === 下载按钮绑定 ===
downloadBtn.onclick = async () => {
  const url = fileUrl.value.trim();
  if (!url) return alert('请输入有效的 URL');

  try {
    downloadBtn.disabled = true;
    pauseDownloadBtn.disabled = false;
    resumeDownloadBtn.disabled = true;
    cancelDownloadBtn.disabled = false;

    downloadController = new DownloadManager(url);
    await downloadController.start();
    await downloadController.mergeAndDownload();
  } catch (err) {
    if (downloadController && !downloadController.isPaused) {
      resetDownloadUI();
    }
  }
};

pauseDownloadBtn.onclick = () => downloadController?.pause();
resumeDownloadBtn.onclick = () => downloadController?.resume();
cancelDownloadBtn.onclick = () => downloadController?.cancel();

function resetDownloadUI() {
  downloadBtn.disabled = false;
  pauseDownloadBtn.disabled = true;
  resumeDownloadBtn.disabled = true;
  cancelDownloadBtn.disabled = true;
  downloadProgressBar.value = 0;
  downloadInfo.textContent = '';
  downloadStatus.textContent = '';
}

function resetUI() {
  startBtn.disabled = !selectedFile;
  pauseBtn.disabled = true;
  resumeBtn.disabled = true;
  cancelBtn.disabled = true;
}

resetUI();